﻿[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Additions.ServiceConstraint.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Additions.ServiceConstraint.debug.js", "application/x-javascript")]

namespace BM.Tools.WebControls.MForm.Controls.Additions
{    
    using System.Collections.ObjectModel;
    using System.ComponentModel;    
    using System.Web.UI;

    using AjaxControlToolkit;

    using BM.Tools.WebControls.MForm.Controls.Items;
    using BM.Tools.WebControls.MForm.ScriptReferences;

    using Helper;

    /// <summary>
    /// A constraint that invokes a script service on client side in order to check validity
    /// </summary>
    [ParseChildren(true, "Parameters")]
    [ClientScriptResource("BM.Tools.WebControls.MForm",
            "BM.Tools.WebControls.MForm.Controls.Additions.ServiceConstraint.js")]
    public class ServiceConstraint : Constraint
    {        
        private readonly Collection<ServiceParameter> parameters = new Collection<ServiceParameter>();

        /// <summary>
        /// Gets service Parameters.
        /// </summary>
        /// <value>
        /// The parameters.
        /// </value>
        [AttributeProperty(Name = "params")]
        public Collection<ServiceParameter> Parameters
        {
            get
            {
                return parameters;
            }
        }

        /// <summary>
        /// Gets or sets the name of the service method.
        /// </summary>
        /// <value>
        /// The service method.
        /// </value>
        [AttributeProperty(Name = "serviceMethod")]
        public string ServiceMethod
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets path to the service that should be invoked.
        /// </summary>
        /// <value>
        /// The service path.
        /// </value>
        [UrlProperty]
        [TypeConverter(typeof(ServicePathConverter))]
        [AttributeProperty(Name = "servicePath")]
        public string ServicePath
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets a script to invoke instead of calling a Web or Page method. This script must evaluate to an object with 'IsValid' and 'ErrorTeext' fields.
        /// </summary>
        [AttributeProperty(Name = "customScript")]
        [DefaultValue(null)]
        public string CustomScript
        {
            get;
            set;
        }

        /// <summary>
        /// checks the validity on server side
        /// </summary>
        /// <param name="item">
        /// The item that is checked for validity on server side
        /// </param>
        /// <returns>
        /// is the constraint valid
        /// </returns>
        public override bool IsValid(OffspringItem item)
        {            
            try
            {
                if (!string.IsNullOrEmpty(CustomScript))
                {
                    return true;
                }

                var invoker = new ScriptServiceInvoker(ResolveClientUrl(ServicePath), ServiceMethod);
                AddParameters(invoker, item);
                var value = ServiceConstraintResponse.FromString(invoker.GetValue<string>());
                return value.IsValid;
            } 
            catch (ScriptServiceInvokerException)
            {
                return false;
            }
        }

        /// <summary>
        /// returns the value of the parameter
        /// </summary>
        /// <param name="parameter">
        /// the parameter
        /// </param>
        /// <param name="item">
        /// The item on which the check is made
        /// </param>
        /// <returns>
        /// the value of the item that is indicated by the parameter's path property
        /// </returns>
        private static string GetParameterValue(ServiceParameter parameter, OffspringItem item)
        {
            var leafItem = item.SelectSingle(parameter.Path) as LeafItem;
            if (leafItem == null)
            {
                return string.Empty;
            }

            return leafItem.Value;
        }

        /// <summary>
        /// adds the parameters to the script invoker
        /// </summary>
        /// <param name="invoker">
        /// The invoker.
        /// </param>
        /// <param name="item">
        /// The item on which the check is made.
        /// </param>
        private void AddParameters(ScriptServiceInvoker invoker, OffspringItem item)
        {
            foreach (var parameter in Parameters)
            {
                invoker.AddParameter(parameter.Name, GetParameterValue(parameter, item));
            }
        }
    }
}